#region Copyright
/// <copyright>
/// Copyright (c) 2007 Ilia Shramko.
/// Released under the terms of the GNU General Public License.
/// </copyright>
#endregion

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;

namespace Osh
{
	public abstract class StreamParser<TStreamSource> : IStreamParser
	{
		private Thread _workerThread;
		private ManualResetEvent _stopEvent;
		private TStreamSource _streamSource;
		private Stream _stream;
		private object _streamLock = new object();
		private TimeSpan _onErrorSleepTimeout = TimeSpan.FromSeconds(3);

		public StreamParser()
		{
			_stopEvent = new ManualResetEvent(false);
		}

		public object StreamSource
		{
			get { return _streamSource; }
		}

		public TStreamSource Source
		{
			get { return _streamSource; }
		}

		protected Stream Stream
		{
			get { return _stream; }
			set { _stream = value; }
		}

		protected WaitHandle StopEvent
		{
			get { return _stopEvent; }
		}

		public bool IsRunning
		{
			get { return _workerThread != null; }
		}

		public void Start(object source)
		{
			Start((TStreamSource)source);
		}

		public virtual void Start(TStreamSource source)
		{
			if(source == null)
				throw new ArgumentNullException("source");

			if(_workerThread != null)
				throw new InvalidOperationException("Parser has already been started.");

			_streamSource = source;

			_stopEvent.Reset();

			_workerThread = new Thread(new ThreadStart(ThreadProc));
			_workerThread.IsBackground = true;
			_workerThread.Priority = ThreadPriority.BelowNormal;
			_workerThread.Start();
		}
		
		public virtual void Stop()
		{
			Thread workerThread = _workerThread;
			if(workerThread == null)
				throw new InvalidOperationException("Parser has already been stoped.");

			_stopEvent.Set();

			if(!workerThread.Join(200))
			{
				// We probably locked reading the stream. Lets close it before waiting further.
				lock(_streamLock)
				{
					if(_stream != null)
						_stream.Close();
				}

				workerThread.Join();
			}
		}

		protected virtual void ThreadProc()
		{
			try
			{
				while(!_stopEvent.WaitOne(0, false))
				{
					try
					{
						OpenStreamAndStartParsingLoop();
					}
					catch(Exception e)
					{
						OnError(e);
						_stopEvent.WaitOne(_onErrorSleepTimeout, false);
					}
				}
			}
			finally
			{
				_workerThread = null;
			}
		}

		protected virtual void OpenStreamAndStartParsingLoop()
		{
			lock(_streamLock)
				_stream = OpenStream(_streamSource);
			try
			{
				ParseStream(_stream);
			}
			finally
			{
				lock(_streamLock)
				{
					_stream.Close();
					_stream = null;
				}
			}
		}

		protected abstract Stream OpenStream(TStreamSource streamSource);

		protected abstract void ParseStream(Stream stream);

		protected virtual void OnError(Exception e)
		{
			if(this.Error != null)
			{
				try
				{
					this.Error(this, new ErrorEventArgs(e));
				}
				catch { }
			}
		}

		protected virtual void OnParsingError(Exception e)
		{
			if(this.ParsingError != null)
			{
				try
				{
					this.ParsingError(this, new ErrorEventArgs(e));
				}
				catch { }
			}
		}

		public event EventHandler<ErrorEventArgs> Error;
		public event EventHandler<ErrorEventArgs> ParsingError;
	}
}
